<?php

namespace yunj\app\admin\service\route;

use think\db\BaseQuery;
use think\facade\Db;
use yunj\app\admin\enum\IsDemo;
use yunj\app\admin\enum\RedisKey;
use yunj\app\admin\enum\State;

/**
 * 路由文件服务
 */
class RouteFileService extends Service {

    // 重置
    public static function reset() {
        // 清理缓存
        RouteRequestService::clearCache();
        // 获取数据
        $datas = self::getTreeDatas();
        $fileContent = self::setFileContent("", '0', 0, '', $datas);

        $fileContent = "<?php
// +----------------------------------------------------------------------
// | 云静Admin
// +----------------------------------------------------------------------
// | Copyright (c) 2019-2022 http://www.iyunj.cn
// +----------------------------------------------------------------------
// | 云静Admin提供个人非商业用途免费使用。
// +----------------------------------------------------------------------
// | Author: Uncle-L <1071446619@qq.com>
// +----------------------------------------------------------------------
// | 请求路由文件
// +----------------------------------------------------------------------

use think\\facade\\Route;
{$fileContent}";
        // 创建目录 或 判断目录是否有写入权限
        dir_writeable_mk(ds_replace(YUNJ_PATH . 'temp/route'));
        $filePath = ds_replace(YUNJ_PATH . 'temp/route/route.php');
        if ($fp = @fopen($filePath, 'w')) {
            @fwrite($fp, $fileContent);
            @fclose($fp);
            // 重新引入路由，防止初始化时生成的路由文件，在权限配置中找不到
            include_once $filePath;
        }
    }

    /**
     * 设置文件内容
     * @param string $fileContent 文件内容
     * @param int|string|mixed $pid 父级id
     * @param int $parentLevel 父级等级
     * @param string $parentNamespace 父级命名空间
     * @param array $datas 数据
     * @return string
     */
    private static function setFileContent(string $fileContent, $pid, int $parentLevel, string $parentNamespace, array $datas): string {
        foreach ($datas as $data) {
            $type = $data['type'];
            // 注意 "group_1" != 0 为 false
            // 字符串以非数字字符开头，会将字符串转换为 0。因此，"group_1" 被转换为数值时会变成 0。所以，当你比较 "group_1" 和 0 时，实际上是在比较 0 和 0，因为它们经过隐式转换后是相等的
            if ($data['pid'] !== $pid || !in_array($type, ['group', 'route'], true)) {
                continue;
            }

            if ($parentLevel == 0) {
                $fileContent .= "\r\n";
            }
            if ($desc = $data['desc']) {
                $fileContent .= "\r\n" . str_repeat("\t", $parentLevel) . "// {$desc}";
            }
            $fileContent .= "\r\n" . str_repeat("\t", $parentLevel);
            if ($type == 'group') {
                $name = handle_route_rule($data['name']);
                $fileContent .= "Route::group('{$name}', function () {";
                $namespace = $parentNamespace . '\\' . $data['namespace'];
                $namespace = handle_route_namespace($namespace); // 去掉连续的'\'
                $fileContent = self::setFileContent($fileContent, $data['id'], $parentLevel + 1, $namespace, $datas);
                $fileContent .= "\r\n" . str_repeat("\t", $parentLevel) . "})";
                // 判断是否有中间件
                $middlewareData = self::handleMiddleware($data);
                if ($middlewareData) {
                    $fileContent .= "->middleware(" . json_encode($middlewareData) . ")";
                }
                $fileContent .= ";";
            } else {
                $route = $parentNamespace . '\\' . $data['route'];
                $route = handle_route_namespace($route); // 去掉连续的'\'
                $rule = handle_route_rule($data['rule']);
                $fileContent .= "Route::rule('{$rule}','{$route}','*')";
                // 判断是否有中间件
                $middlewareData = self::handleMiddleware($data);
                if ($middlewareData) {
                    $fileContent .= "->middleware(" . json_encode($middlewareData) . ")";
                }
                // 判断是否有name
                if ($name = $data['name']) {
                    $fileContent .= "->name('{$name}')";
                }
                $fileContent .= ";";
            }
        }
        return $fileContent;
    }

    // 处理中间件
    private static function handleMiddleware(array $data): array {
        $middlewareData = [];
        if (is_json($data['middleware'], $middlewareArr, true)) {
            $middlewareData = $middlewareArr;
        }
        foreach ($middlewareData as $k => $v) {
            if (!RouteService::checkMiddlewareItem($v)) {
                unset($middlewareData[$k]);
            }
            if ($v['params']) {
                $middlewareData[$k] = [$v['class'], explode(',', $v['params'])];
            } else {
                $middlewareData[$k] = $v['class'];
            }
        }
        return $middlewareData;
    }

    // 获取树形数据
    private static function getTreeDatas() {
        $prefix = Db::connect()->getConfig('prefix');

        $where = [
            ['state', '=', State::NORMAL]
        ];
        // 不引入demo
        if (!yunj_config('admin.use_demo')) {
            $where[] = ['is_demo', '=', IsDemo::NO];
        }
        $datas = Db::table("(
            select 'group' as 'type',concat('group_',id) as id,if(pid>0,concat('group_',pid),0) as pid,name,namespace,middleware,`desc`,'' as rule,'' as route,sort,is_demo,state from {$prefix}admin_route_group
            union all 
            select 'route' as 'type',concat('route_',id) as id,if(group_id>0,concat('group_',group_id),0) as pid,name,'' as namespace,middleware,`desc`,rule,route,sort,is_demo,state from {$prefix}admin_route
        ) as tmp")
            ->where($where)
            ->order(['sort' => 'asc', 'id' => 'asc'])->select()->toArray();
        return $datas;
    }

}